﻿using AutoMapper;
using SolidDemo.Data;
using SolidDemo.Data.Enums;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic;
using System.Text;
using System.Threading.Tasks;
using SolidDemo.Business.Interfaces;
using SolidDemo.Data.Contexts;
using SolidDemo.Data.Shared;

namespace SolidDemo.Business.Repositories
{
    public class GenericRepository : GomtuuRepository, IGenericRepository
    {
        public GenericRepository(MainContext db, IDataHelper dataHelper, IMessageHelper messageHelper) : base(db, dataHelper, messageHelper) { }

        public T LoadById<T>(int id, string label = "Id", List<string> includes = null) where T : class
        {
            List<T> entities;
            var where = string.Format("{0}={1}", label, id);

            if (includes != null && includes.Count > 0)
            {
                var includesString = string.Join(",", includes);
                entities = _db.Set<T>().Include(includesString).Where(where).ToList();
            }
            else
            {
                entities = _db.Set<T>().Where(where).ToList();
            }

            if (entities.Count == 0) return null;
            if (entities.Count <= 1) return entities[0];

            var message = string.Format("More than one '{0}' entity found for '{1}' with value '{2}'", typeof(T).FullName, label, id);

            LogAndSend(message, ELogPriority.Critical);

            return entities[0];
        }

        public T LoadByCode<T>(string code, string label = "Code", List<string> includes = null) where T : class
        {
            List<T> entities;
            var where = string.Format("{0}=\"{1}\"", label, code);

            if (includes != null && includes.Count > 0)
            {
                var includesString = string.Join(",", includes);
                entities = _db.Set<T>().Include(includesString).Where(where).ToList();
            }
            else
            {
                entities = _db.Set<T>().Where(where).ToList();
            }

            if (entities.Count == 0) return null;
            if (entities.Count <= 1) return entities[0];

            var message = string.Format("More than one '{0}' entity found for '{1}' with value '{2}'", typeof(T).FullName, label, code);

            LogAndSend(message, ELogPriority.Critical);

            return entities[0];
        }

        public List<T> LoadAll<T>(List<string> includes = null) where T : class
        {
            return _db.Set<T>().ToList();
        }

        public IEnumerable<T> FilterByString<T>(string label, string value, List<string> includes = null) where T : class
        {
            var where = string.Format("{0}=\"{1}\"", label, value);
            var list = _db.Set<T>().Where(where);
            return list;
        }

        public void Save<T>(T entity) where T : class
        {
            var type = entity.GetType();
            var properties = type.GetProperties();

            var idProperty = properties.SingleOrDefault(p => p.Name.ToLower() == "id");
            if (idProperty == null)
            {
                var message = string.Format("Unable to locate Id property for entity type '{0}'", type);
                LogAndSend(message, ELogPriority.Critical);
                return;
            }

            var value = (int)idProperty.GetValue(entity);

            if (value == 0)
            {
                _db.Set<T>().Add(entity);
            }
            else
            {
                var existing = _db.Set<T>().Find(value);

                //if (excludeProperties == null) excludeProperties = new List<string>();

                var excludeProperties = new List<string>{"Id"};

                //Mapper.CreateMap<T, T>();
                //Mapper.Map(entity, existing);
                //Mapper.CreateMap<T, T>().ForMember("Id", opt => opt.Ignore());
                //existing = Mapper.Map<T>(entity);
		
                _dataHelper.MapProperties(entity, existing, excludeProperties);
            }

            _db.SaveChanges();
        }

        // We don't want an email failure to affect the user experience so just log what went wrong instead. jb
        private void LogAndSend(string message, string priority)
        {
            _db.Logs.Add(new Log { Created = DateTime.Now, Message = message, Priority = priority });

            try
            {
                _messageHelper.SendErrorEmail(message, priority);
            }
            catch (Exception ex)
            {
                _db.Logs.Add(new Log { Created = DateTime.Now, Message = ex.Message, Priority = ELogPriority.Critical });
            }
        }

        //public void SaveByKey<T>(T entity, string keyName = "Id", List<string> excludeProperties = null) where T : class
        //{
        //    var type = entity.GetType();
        //    var properties = type.GetProperties();

        //    var keyProperty = properties.SingleOrDefault(p => p.Name.ToLower() == keyName);
        //    if (keyProperty == null)
        //    {
        //        var message = string.Format("Unable to locate the key property '{0}' for entity type '{1}'", keyName, type);
        //        LogAndSend(message, ELogPriority.Critical);
        //        return;
        //    }

        //    var value = (int)keyProperty.GetValue(entity);

        //    if (value == 0)
        //    {
        //        _db.Set<T>().Add(entity);
        //    }
        //    else
        //    {
        //        var existing = _db.Set<T>().SingleOrDefault(value);

        //        if (excludeProperties == null) excludeProperties = new List<string>();

        //        excludeProperties.Add("Id");

        //        Mapper.CreateMap<T, T>();
        //        existing = Mapper.Map<T>(entity);
        //        //_dataHelper.MapProperties(entity, existing, excludeProperties);
        //    }

        //    _db.SaveChanges();
        //}
    }
}
